#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import logging
import datetime
import os
import re
from pytz import timezone, utc

from google.appengine.api import users
from google.appengine.api import mail
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import login_required
from google.appengine.ext.webapp.util import run_wsgi_app
from django.utils import simplejson

# Set to true if we want to have our webapp print stack traces, etc
_DEBUG = False


# Add our custom Django template filters to the built in filters
template.register_template_library('templatefilters')

class Kategorija(db.Model):
	eilnr = db.IntegerProperty(required=True)
	kodas = db.StringProperty(required=True)
	pavadinimas = db.StringProperty(required=True)
	pastabos = db.TextProperty()
	raktiniai = db.StringProperty()


class Knyga(db.Model):
	numeris = db.IntegerProperty(required=True)
	autorius = db.StringProperty()
	antraste = db.StringProperty(required=True)
	leidykla = db.StringProperty()
	miestas = db.StringProperty()
	metai = db.IntegerProperty()
	kategorija = db.Reference(Kategorija)
	pastabos = db.StringProperty()
	irasyta = db.DateTimeProperty(auto_now_add=True)
	atnaujinta = db.DateTimeProperty(auto_now=True)


class KnygosIrasas(db.Model):
	knyga = db.Reference(Knyga, required=True)
	skaitytojas = db.StringProperty(required=True)
	isduota = db.DateTimeProperty(auto_now_add=True)
	grazinta = db.DateTimeProperty()


class Narys(db.Model):
	user = db.UserProperty(required=True)
	
	@staticmethod
	def user_has_access(user):
		query = db.Query(Narys).filter('user =', user)
		return query.get()
	
	@staticmethod
	def current_user_has_access():
		return Narys.user_has_access(users.get_current_user())


class BaseRequestHandler(webapp.RequestHandler):
	vilnius = timezone('Europe/Vilnius')
	_OUTPUT_TYPES = {
		'default': ['text/html', 'html'],
		'html': ['text/html', 'html'],
		'atom': ['application/atom+xml', 'xml'],}

	def set_output_name(self):
		self.output_name = self.request.get('output')
		if self.output_name not in BaseRequestHandler._OUTPUT_TYPES:
			self.output_name = 'default'
		if not Narys.current_user_has_access():
			if self.output_name == 'default':
				self.output_name = 'html'
		
	def generate(self, template_name, template_values={}):
		try:
			self.output_name
		except AttributeError:
			self.set_output_name()
		output_type = BaseRequestHandler._OUTPUT_TYPES[self.output_name]

		self.response.headers['Content-Type'] = output_type[0]
		
		nariai = list(db.Query(Narys))
		if len(nariai) == 0:
			user = users.get_current_user()
			if user:
				narys = Narys(user=user)
				narys.put()
				nariai = [narys]
			else:
				nariai = None

		values = {
				'request': self.request,
				'user': users.get_current_user(),
				'login_url': users.create_login_url(self.request.uri),
				'logout_url': users.create_logout_url('http://%s/' % (
						self.request.host,)),
				'debug': _DEBUG, #self.request.get('deb'),
				'application_name': 'Vienuolynėlio bibliotekėlė',
				'bendradarbiai': nariai }
		values.update(template_values)
		directory = os.path.dirname(__file__)
		path = os.path.join(directory, os.path.join('templates', '%s_%s.%s' % (template_name, self.output_name, output_type[1])))
		self.response.out.write(template.render(path, values, debug=_DEBUG))


class KnyguSarasasPage(BaseRequestHandler):
	def get(self):
		paieska = self.request.get('paieska')
		page = self.request.get('puslapis')
		if (not page):
			page = 1
		else:
			page = int(page)

		praeitas_numeris = 0
		atnaujinta = datetime.datetime(1980,1,1)
		knygos = list(db.Query(Knyga).order('-numeris'))

		for knyga in knygos:
			if knyga.atnaujinta > atnaujinta:
				atnaujinta = knyga.atnaujinta
			if praeitas_numeris - 1 != knyga.numeris:
				numeris = knyga.numeris
				logging.info('Numeris kandidatas %s' % numeris)
			praeitas_numeris = knyga.numeris

		if paieska:
			rastos_knygos = []
			for knyga in knygos:
				if str(knyga.numeris).find(paieska) != -1 or knyga.antraste.lower().find(paieska.lower()) != -1 or (knyga.autorius and knyga.autorius.lower().find(paieska.lower()) != -1) or (knyga.leidykla and knyga.leidykla.lower().find(paieska.lower()) != -1) or (knyga.miestas and knyga.miestas.lower().find(paieska.lower()) != -1) or (knyga.metai and str(knyga.metai).find(paieska) != -1) or (knyga.pastabos and knyga.pastabos.lower().find(paieska.lower()) != -1):
					rastos_knygos.append(knyga)
			knygos = rastos_knygos

		item_per_page = 16
		start_index = (page - 1) * item_per_page
		total_items = len(knygos)
		total_pages = (total_items-1) / item_per_page + 1
		from_page = page - 4
		to_page = page + 4
		if from_page < 1:
			to_page -= from_page
			from_page = 1
		if to_page > total_pages:
			to_page = total_pages
		page_list = []
		for i in range(from_page, to_page+1):
			page_list.append(i)

		praeitas = page - 1
		if praeitas < 1:
			praeitas = None
		sekantis = page + 1
		if sekantis > total_pages:
			sekantis = None

		self.set_output_name()
		if self.output_name != 'atom':
			knygos = knygos[start_index:start_index+item_per_page]

		for i in range(len(knygos)):
			knygos[i].irasyta_local = utc.localize(knygos[i].irasyta).astimezone(BaseRequestHandler.vilnius)
			knygos[i].atnaujinta_local = utc.localize(knygos[i].irasyta).astimezone(BaseRequestHandler.vilnius)

		args = []
		for key in self.request.arguments():
			if key != 'puslapis':
				args.append(key + "=" + self.request.get(key))
		args.append("puslapis=")
		uzklausa = "?" + "&amp;".join(args)

		self.generate('index', {
				'knygos': knygos,
				'atnaujinta': atnaujinta,
				'viso': total_items,
				'praeitas': praeitas,
				'dabartinis': page,
				'sekantis': sekantis,
				'puslapiai': page_list,
				'paieska': paieska,
				'uzklausa': uzklausa,
				'numeris': numeris + 1 })


class KnygaPage(BaseRequestHandler):
	
	def get(self):
		knyga = Knyga.get(self.request.get('id'))
		if not knyga:
			self.error(403)
			return

		paskutinis_irasas = { 'grazinta': True }

		irasai = list(knyga.knygosirasas_set.order('isduota'))
		for i in range(0, len(irasai)):
			irasai[i].isduota_local = utc.localize(irasai[i].isduota).astimezone(BaseRequestHandler.vilnius)
			if irasai[i].grazinta:
				irasai[i].grazinta_local = utc.localize(irasai[i].grazinta).astimezone(BaseRequestHandler.vilnius)

			if irasai[i].grazinta:
				irasai[i].atnaujinta = irasai[i].grazinta
			else:
				irasai[i].atnaujinta = irasai[i].isduota

			paskutinis_irasas = irasai[i]

		self.generate('knyga', {
				'knyga': knyga,
				'irasai': irasai,
				'paskutinis_irasas': paskutinis_irasas })


class KategorijosPage(BaseRequestHandler):
	def get(self):
		kategorijos = list(db.Query(Kategorija).order('eilnr'))
		self.generate('kategorijos', {
				'kategorijos': kategorijos })

class KategorijuPaieska(BaseRequestHandler):
	def get(self):
		radiniai = { "results" : [] }
		input = self.request.get('input')
		if input:
			kategorijos = db.Query(Kategorija)
			
			regexp = re.compile(input, re.IGNORECASE)
			for kategorija in kategorijos:
				if regexp.search(kategorija.pavadinimas) or regexp.search(kategorija.kodas) or (kategorija.raktiniai and regexp.search(kategorija.raktiniai)) or (kategorija.pastabos and regexp.search(kategorija.pastabos)):
					radiniai["results"].append({ "id": str(kategorija.key()), "value": "%s %s" % (kategorija.kodas, kategorija.pavadinimas), "info": ""})

		self.response.headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'
		# Date in the past
		self.response.headers['Last-Modified'] = datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S') + ' GMT' # always
		self.response.headers['Cache-Control'] = 'no-cache, must-revalidate' # HTTP/1.1
		self.response.headers['Pragma'] = 'no-cache' # HTTP/1.0
		self.response.headers['Content-Type'] = 'application/json'

		self.response.out.write(simplejson.dumps(radiniai, indent=2))
		
class KategorijosSukurimasAction(BaseRequestHandler):
	def post(self):
		kodas = self.request.get('kodas')
		pavadinimas = self.request.get('pavadinimas')
		pastabos = self.request.get('pastabos')
		raktiniai = self.request.get('raktiniai')
		if not kodas or not pavadinimas or not Narys.current_user_has_access():
			self.error(403)
			return

		kategorija_key = self.request.get('kategorija')
		if kategorija_key:
			kategorija = Kategorija.get(kategorija_key)
			if not kategorija:
				self.error(403)
				return

			kategorija.kodas = kodas
			kategorija.pavadinimas = pavadinimas
			kategorija.pastabos = pastabos
			kategorija.raktiniai = raktiniai
		else:
			kategorijos = list(db.Query(Kategorija))
			if len(kategorijos) > 0:
				nr = max([kategorija.eilnr for kategorija in kategorijos]) + 1
			else:
				nr = 1
			kategorija = Kategorija(eilnr=nr, kodas=kodas, pavadinimas=pavadinimas, pastabos=pastabos, raktiniai=raktiniai)

		kategorija.put()

		next = self.request.get('next')
		if next:
			self.redirect(next)
		else:
			self.redirect('/kategorijos')

class KnygosSukurimasAction(BaseRequestHandler):
	def post(self):
		numeris = int(self.request.get('numeris'))
		autorius = self.request.get('autorius')
		antraste = self.request.get('antraste')
		leidykla = self.request.get('leidykla')
		miestas = self.request.get('miestas')
		metai = self.request.get('metai')
		if metai:
			metai = int(metai)
		else:
			metai = None
		klasifikacija = self.request.get('klasifikacija')
		if klasifikacija: 
			kategorija = Kategorija.get(klasifikacija)
		else:
			kategorija = None
		pastabos = self.request.get('pastabos')
		if not antraste or not numeris or not Narys.current_user_has_access():
			self.error(403)
			return

		knyga_key = self.request.get('knyga')
		if knyga_key:
			knyga = Knyga.get(knyga_key)
			if not knyga:
				self.error(403)
				return

			knyga.numeris = numeris
			knyga.autorius = autorius
			knyga.antraste = antraste
			knyga.leidykla = leidykla
			knyga.miestas = miestas
			knyga.metai = metai
			knyga.kategorija = kategorija
			knyga.pastabos = pastabos
		else:
			if db.Query(Knyga).filter('numeris =', numeris).get():
				self.error(403)
				return
			knyga = Knyga(numeris=numeris, autorius=autorius, antraste=antraste, leidykla=leidykla, miestas=miestas, metai=metai, kategorija=kategorija, pastabos=pastabos)

		knyga.put()

		next = self.request.get('next')
		if next:
			self.redirect(next)
		else:
			self.redirect('/')
			#self.redirect('/knyga?id=' + str(knyga.key()))


class KnygosIsdavimasAction(BaseRequestHandler):
	def post(self):
		skaitytojas = self.request.get('skaitytojas')
		if not skaitytojas or not Narys.current_user_has_access():
			self.error(403)
			return

		# Get the existing irasas that we are editing
		irasas_key = self.request.get('irasas')
		if irasas_key:
			irasas = KnygosIrasas.get(irasas_key)
			if not irasas:
				self.error(403)
				return
			knyga = irasas.knyga
			irasas.skaitytojas = db.Text(skaitytojas)
		else:
			irasas = None
			knyga = Knyga.get(self.request.get('knyga'))
			irasas = KnygosIrasas(skaitytojas=db.Text(skaitytojas), knyga=knyga)

		irasas.put()

		knyga.put()

		next = self.request.get('next')
		if next:
			self.redirect(next)
		else:
			self.response.headers['Content-Type'] = 'text/plain'
			self.response.out.write(str(irasas.key()))


class KnygosGrazinimasAction(BaseRequestHandler):
	def post(self):
		irasas = KnygosIrasas.get(self.request.get('id'))
		if not irasas or not Narys.current_user_has_access():
			self.error(403)
			return

		irasas.grazinta = datetime.datetime.now()
		irasas.put()

		next = self.request.get('next')
		if next:
			self.redirect(next)
		else:
			self.response.headers['Content-Type'] = 'text/plain'
			self.response.out.write(str(irasas.key()))


class PridetiNariAction(BaseRequestHandler):
	def post(self):
		email = self.request.get('email')
		if not email or not Narys.current_user_has_access():
			self.error(403)
			return

		# Don't duplicate entries in the permissions datastore
		user = users.User(email)
		if not Narys.user_has_access(user):
			narys = Narys(user=user)
			narys.put()
		next = self.request.get('next')
		if next:
			self.redirect(next)

class KategorijuIstrynimasAction(BaseRequestHandler):
	def post(self):
		action = self.request.get('action')
		kategorijos = self.request.get('kategorija', allow_multiple=True)
		if not action.encode( "utf-8" ) in ['Ištrinti'] or not Narys.current_user_has_access():
			self.error(403)
			return

		for key in kategorijos:
			kategorija = Kategorija.get(key)

			if not kategorija:
				self.error(403)
				return

			knygos = db.Query(Knyga).filter('kategorija =', kategorija)
			for knyga in knygos:
				knyga.kategorija = None
				knyga.put()

			kategorija.delete()

		next = self.request.get('next')
		if next:
			self.redirect(next)

class KnyguSarasasAction(BaseRequestHandler):
	def post(self):
		action = self.request.get('action')
		knygos = self.request.get('knyga', allow_multiple=True)
		if not action.encode( "utf-8" ) in ['Ištrinti'] or not Narys.current_user_has_access():
			self.error(403)
			return

		for key in knygos:
			knyga = Knyga.get(key)

			if not knyga:
				self.error(403)
				return

			for irasas in knyga.knygosirasas_set:
				irasas.delete()
			knyga.delete()

		next = self.request.get('next')
		if next:
			self.redirect(next)


class KnygaAction(BaseRequestHandler):
	def post(self):
		action = self.request.get('action')
		irasai = self.request.get('irasas', allow_multiple=True)
		if not action.encode("utf-8") in ['Ištrinti'] or not Narys.current_user_has_access():
			self.error(403)
			return

		for key in irasai:
			irasas = KnygosIrasas.get(key)

			if not irasas:
				self.error(403)
				return

			if action.encode("utf-8") == 'Ištrinti':
				irasas.delete()

		next = self.request.get('next')
		if next:
			self.redirect(next)


class AtsargineKopijaAction(BaseRequestHandler):
	def zip(self, files):
		import zipfile
		import StringIO
		stream = StringIO.StringIO()
		zip = zipfile.ZipFile(stream, "w")
		for file in files:
			zip.writestr(file[0], file[1])
		zip.close()
		contents = stream.getvalue()
		stream.close()
		return contents

	def get(self):
		attachments = [
			('kategorijos.csv', KategorijosImportExportAction.export()),
			('knygos.csv', KnygosImportExportAction.export())
		]
		# zip attachements. app engine does not allow to send 'zip' extension
		#attachments = [('biblioteka.zip', self.zip(attachments))]
		mail.send_mail(
			sender="teofilis@gmail.com",
			to="communio.trinitatis@gmail.com",
	 		subject="Bibliotekėlės atsarginė kopija",
			body="Atsarginės kopijos nekenkia",
			attachments=attachments)

class KnygosImportExportAction(BaseRequestHandler):
	@staticmethod
	def export():
		ret = ''
		knygos = db.Query(Knyga).order('numeris')
		for knyga in knygos:
			row = [str(knyga.numeris)]

			if knyga.autorius:
				row.append(knyga.autorius.encode('utf-8'))
			else:
				row.append('')

			row.append(knyga.antraste.encode('utf-8'))

			if knyga.leidykla:
				row.append(knyga.leidykla.encode('utf-8'))
			else:
				row.append('')

			if knyga.miestas:
				row.append(knyga.miestas.encode('utf-8'))
			else:
				row.append('')

			if knyga.metai:
				row.append(str(knyga.metai))
			else:
				row.append('')

			if knyga.kategorija:
				row.append(knyga.kategorija.kodas.encode('utf-8'))
			else:
				row.append('')

			if knyga.pastabos:
				row.append(knyga.pastabos.encode('utf-8'))
			else:
				row.append('')

			row.append(knyga.irasyta.strftime('%Y-%m-%d %H:%M:%S'))

			ret += '\t'.join(row) + '\n'
		return ret

	def get(self):
		self.response.headers['Content-Type'] = 'text/plain'
		self.response.out.write(KnygosImportExportAction.export())

	def post(self):
		failas = unicode(self.request.get('failas'), encoding='utf-8')
		istrinti = self.request.get('istrinti')
		if not failas or not Narys.current_user_has_access():
			self.error(403)
			return

		if istrinti:
			knygos = db.Query(Knyga)
			db.delete(knygos)
			

		eilutes = failas.split("\n")

		for eilute in eilutes:
			if eilute:
				laukeliai = eilute.split("\t")

				numeris = int(laukeliai[0])

				if laukeliai[1]:
					autorius = laukeliai[1]
				else:
					autorius = None

				antraste = laukeliai[2]

				if laukeliai[3]:
					leidykla = laukeliai[3]
				else:
					leidykla = None

				if laukeliai[4]:
					miestas = laukeliai[4]
				else:
					miestas = None

				if laukeliai[5]:
					metai = int(laukeliai[5])
				else:
					metai = None

				if laukeliai[6]:
					query = db.Query(Kategorija).filter('kodas =', laukeliai[6])
					kategorija = query.get()
				else:
					kategorija = None

				if laukeliai[7]:
					pastabos = laukeliai[7]
				else:
					pastabos = None

				irasyta = datetime.datetime.strptime(laukeliai[8], '%Y-%m-%d %H:%M:%S')

				knyga = Knyga(numeris=numeris, autorius=autorius, antraste=antraste, leidykla=leidykla, miestas=miestas, metai=metai, kategorija=kategorija, pastabos=pastabos, irasyta=irasyta)
				knyga.put()

		next = self.request.get('next')
		if next:
			self.redirect(next)

class KategorijosImportExportAction(BaseRequestHandler):
	@staticmethod
	def export():
		ret = ''
		kategorijos = list(db.Query(Kategorija).order('eilnr'))
		for kategorija in kategorijos:
			ret += kategorija.kodas + "\t" + kategorija.pavadinimas
			if (kategorija.raktiniai):
				ret += "\t" + kategorija.raktiniai
			if (kategorija.pastabos):
				pastabos = re.sub('\n', '\n\t', kategorija.pastabos)
				ret += "\n\t" + pastabos
			ret += "\n"
		return ret.encode('utf-8')

	def get(self):
		self.response.headers['Content-Type'] = 'text/plain'
		self.response.out.write(KategorijosImportExportAction.export())


	def post(self):
		failas = unicode(self.request.get('failas'), encoding='utf-8')
		istrinti = self.request.get('istrinti')
		if not failas or not Narys.current_user_has_access():
			self.error(403)
			return

		if istrinti:
			kategorijos = db.Query(Kategorija)
			for kategorija in kategorijos:
				knygos = db.Query(Knyga).filter('kategorija =', kategorija)
				for knyga in knygos:
					knyga.kategorija = None
					knyga.put()
			db.delete(kategorijos)

		kategorijos = list(db.Query(Kategorija))
		if len(kategorijos) > 0:
			nr = max([kategorija.eilnr for kategorija in kategorijos]) + 1
		else:
			nr = 1

		eilutes = failas.split("\n")
		rinkinys = None

		for eilute in eilutes:
			if eilute:
				kodas = None
				pavadinimas = None
				raktiniai = None
				pastabos = None

				m = re.match(r"^([^\t]+)\t([^\t]*)\t([^\t]*)", eilute)
				if m:
					kodas = m.group(1)
					pavadinimas = m.group(2)
					raktiniai = m.group(3)
				else:
					m = re.match(r"^([^\t]+)\t([^\t]*)", eilute)
					if m:
						kodas = m.group(1)
						pavadinimas = m.group(2)
					else:
						m = re.match(r"^\t(.*)", eilute)
						if m:
							pastabos = m.group(1)
						else:
							raise Exception("Parsinimo klaida %s" % eilute)

				if kodas:
					if rinkinys:
						kategorija = Kategorija(eilnr=nr, kodas=rinkinys[0], pavadinimas=rinkinys[1], pastabos=rinkinys[2], raktiniai=rinkinys[3])
						kategorija.put()
						nr += 1

					rinkinys = [kodas, pavadinimas, pastabos, raktiniai]
				elif pastabos:
					if rinkinys[2]:
						rinkinys[2] = "%s\n%s" % (rinkinys[2], pastabos)
					else:
						rinkinys[2] = pastabos

		if rinkinys:
			kategorija = Kategorija(eilnr=nr, kodas=rinkinys[0], pavadinimas=rinkinys[1], pastabos=rinkinys[2], raktiniai=rinkinys[3])
			kategorija.put()
			nr += 1

		next = self.request.get('next')
		if next:
			self.redirect(next)


def main():
	application = webapp.WSGIApplication([
			('/', KnyguSarasasPage),
			('/knyga', KnygaPage),
			('/kategorijos', KategorijosPage),
			('/kategorijupaieska', KategorijuPaieska),
			('/knyguimportavimas.do', KnygosImportExportAction),
			('/knygos.csv', KnygosImportExportAction),
			('/kategorijuimportavimas.do', KategorijosImportExportAction),
			('/kategorijos.csv', KategorijosImportExportAction),
			('/kategorijossukurimas.do', KategorijosSukurimasAction),
			('/knygossukurimas.do', KnygosSukurimasAction),
			('/knygosisdavimas.do', KnygosIsdavimasAction),
			('/knygosgrazinimas.do', KnygosGrazinimasAction),
			('/pridetinari.do', PridetiNariAction),
			('/atsarginekopija.do', AtsargineKopijaAction),
			('/kategorijuistrynimas.do', KategorijuIstrynimasAction),
			('/knygusarasasaction.do', KnyguSarasasAction),
			('/knygaaction.do', KnygaAction)], debug=_DEBUG)
	run_wsgi_app(application)


if __name__ == '__main__':
	main()
